/*************************************************************************
	> File Name: 8.6Synchronized--信号量.c
	> Author: Potter
	> Mail: tobewhatwewant@gmail.com
	> Created Time: 2014年09月18日 星期四 09时16分15秒
 ************************************************************************/

/*
* INCLUDE: /usr/include/semaphore.h 
*
* 初始化线程:
*   int sem_init(sem_t * sem, int pshared, unsigned int value);
*     sem为指向信号量结构的一个指针;pshared不为0时此信号在进程间共享，
*    否则只能为当前进程的所有线程共享;value给出了信号量的初始值
*
* 阻塞线程:
*   int sem_wait(sem_t * sem);
*     被用来阻塞当前线程知道信号量sem的值大于0,解除阻塞后将sem的值减一
*    表明公共资源经使用后减少。函数 sem_trywait(sem_t * sem);是
*    sem_wait(sem_t * sem)的非阻塞版本，它直接将信号量sem的值减一，相当于p
*    操作。
*
* 增加信号量的值:
*   int sem_post(sem_t * sem);
*     当线程阻塞在这个信号量上时，调用这个函数会使其中的一个线程不再阻塞，
*     选择机制同样是由线程的调度策略决定的，相当于V操作。
*
* 释放信号量资源:
*   int sem_destroy(sem_t * sem);
* 
* */

#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>
#include<time.h>
#include<stdlib.h>

sem_t full, empty; /*信号量,empty的值表示空缓冲区的个数,full表示空缓冲区消息的个数，用于生成者和消费者同步*/
pthread_mutex_t mutex; /*互斥量，用于访问缓冲区互斥，缓冲区为临界资源*/
#define BUFFERSIZE 5 /*5个缓冲区*/
struct msgbuf { /*缓冲区结构*/
    pid_t id; /*线程id*/
    time_t mytime; /*时间*/
};
struct msgbuf msg[BUFFERSIZE];
int in = 0, out = 0;

/* 生产者producer(),用于生产者计算当前的时间，把时间、第几次时间的序号
 * （循环次数）和线程ID作为一个消息，把消息放入缓冲区，每个线程产生10消息
 * */
 void * producer(void * arg) {
    int i;
    time_t rt;
     for (i=1; i<=10; i++) {
         sem_wait(&empty); /*缓冲区个数减1,若缓冲区个数小于等于0,则阻塞本线程*/
         pthread_mutex_lock(&mutex); /*加锁，申请缓冲区资源,若有其他线程在读写缓冲区，则阻塞线程*/
         // 临界区
         msg[in].id = pthread_self(); /*获得线程ID，写入缓冲区*/
         time(&(msg[in].mytime)); /*获取时间信息，写入缓冲区*/
         in = (++in) % 5;
         fprintf(stdout, "生产者%d第%d次写消息---, id = %u, time is: %s\n", (int)arg, i, (unsigned)(msg[out].id), ctime(&(msg[out].mytime)));
         pthread_mutex_unlock(&mutex); /*解锁，释放缓冲区资源，若有线程在等待使用缓冲区，则唤醒该线程*/
         sem_post(&full); /*消息数加1,若有等待读缓冲区消息的消费者线程，则唤醒该线程*/
         srand((unsigned)time(&rt));
         sleep(rand()%5);
     }
 }

 /*消费者consumer，用于消费者从缓冲区读出一个消息并显示消息，每个线程消费10个消息*/
 void * consumer (void * arg) {
     int i;
     time_t rt;
     for (i=1; i<=10; i++) {
         sem_wait(&full); /*消息数减1,若消息个数小于等于0,则阻塞本线程*/
         pthread_mutex_lock(&mutex); /*加锁，申请缓冲区资源，若有其他线程读取缓冲区资源，则阻塞该线程*/
         fprintf(stdout, "消费者%d第%d次读取消息---, id = %u, time is: %s\n", (int)arg, i, (unsigned)(msg[out].id), ctime(&(msg[out].mytime)));
         out = (++out) % 5;
         pthread_mutex_unlock(&mutex);
         sem_post(&empty); /*空缓冲数加1,若有写缓冲区消息的生产者，则唤醒该线程*/
         srand((unsigned)time(&rt));
         sleep(3 + rand() % 5);
     }
 }

 int main(int argc, char * argv[]) {
     pthread_t pid1, pid2;
     pthread_t cid1, cid2;
     sem_init(&full, 0, 0); /*信号量初始化，初始消息个数为0*/
     sem_init(&empty, 0, 5); /*信号量初始化，初始缓冲区个数为5*/

     pthread_mutex_init(&mutex, NULL);

     /*创建2个生产者线程和2个消费者线程*/
     pthread_create(&pid1, NULL, producer, 1);
     pthread_create(&pid2, NULL, producer, 2);
     pthread_create(&cid1, NULL, consumer, 1);
     pthread_create(&cid2, NULL, consumer, 2);

     pthread_join(pid1, NULL); /*等待地一个生产者线程结束*/
     pthread_join(pid2, NULL); /*等待第二个生产者线程结束*/
     pthread_join(cid1, NULL); /*等待地一个消费者线程结束*/
     pthread_join(cid2, NULL);

     pthread_mutex_destroy(&mutex); /*释放互斥量*/
     sem_destroy(&full); /*释放信号量*/
     sem_destroy(&empty);

     return 0;
 }
